
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <jni.h>
#include "com_xiyuan_smartutils_reflect_JavaUnsafe.h"

typedef struct {
    char* name;
    char* Signature;
    jmethodID method;

} MTHD;

// 全局缓存结构体
typedef struct {
    jclass cls;
    jobject instance;
    JavaVM* jvm;
    JNIEnv* env;
    jobject jniInfo;
    int majorVersion;
} UnsafeContext;


extern "C" {
    static jclass NullPointerException = nullptr;
    void throwNullPointerException(JNIEnv* env, const char* message) {
        if (NullPointerException == nullptr) {
            jclass localCls = env->FindClass("java/lang/NullPointerException");
            NullPointerException = (jclass)env->NewGlobalRef(localCls); //
            env->DeleteLocalRef(localCls);
        }

        if (NullPointerException != nullptr) {
            env->ThrowNew(NullPointerException, message);
        }

    }



    static MTHD registerNatives                 = { (char*)"registerNatives",                            (char*)"()V", nullptr };
    static MTHD unregisterNatives               = { (char*)"unregisterNatives",                          (char*)"()V", nullptr };
    static MTHD getJavaUnsafe0                  = { (char*)"getJavaUnsafe0",                             (char*)"()Ljava/lang/Object;", nullptr };
    static MTHD defineClass0                    = { (char*)"defineClass0",                               (char*)"(Ljava/lang/String;[BIILjava/lang/ClassLoader;Ljava/security/ProtectionDomain;)Ljava/lang/Class;", nullptr };


    static MTHD classLoader_defineClass         = { (char*)"defineClass",                                (char*)"(Ljava/lang/String;[BIILjava/security/ProtectionDomain;)Ljava/lang/Class;", nullptr };
    static MTHD constructor_setAccessible       = { (char*)"setAccessible",                              (char*)"(Z)V", nullptr };
    static MTHD method_setAccessible            = { (char*)"setAccessible",                              (char*)"(Z)V", nullptr };
    static MTHD field_setAccessible             = { (char*)"setAccessible",                              (char*)"(Z)V", nullptr };
    static MTHD module_addOpens                 = { (char*)"addOpens",                                   (char*)"(Ljava/lang/String;Ljava/lang/Module;)Ljava/lang/Module;", nullptr };
    static MTHD module_implAddExportsOrOpens    = { (char*)"implAddExportsOrOpens",                      (char*)"(Ljava/lang/String;Ljava/lang/Module;ZZ)V", nullptr };


    static char* unsafeInitInfo   = (char*)"unsafeInitInfo";
    static UnsafeContext ctx;
    static int is_register_native = 0;

    // 获取put方法ID
    static jmethodID mapPutMethod = nullptr;
    static jmethodID mapGetMethod = nullptr;

    jobject newHashMap(JNIEnv* env) {
        jclass hashMapClass = env->FindClass("java/util/HashMap");
        if (hashMapClass == nullptr) return nullptr;

        // 获取构造函数ID并创建HashMap实例
        jmethodID hashMapConstructor = env->GetMethodID(hashMapClass, "<init>", "()V");
        if (hashMapConstructor == nullptr) return nullptr;
        jobject hashMap = env->NewObject(hashMapClass, hashMapConstructor);
        if (hashMap == nullptr) return nullptr;

        // 获取put方法ID
        if (!mapPutMethod) mapPutMethod = env->GetMethodID(hashMapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
        if (mapPutMethod == nullptr) return nullptr;

        if (!mapGetMethod) mapGetMethod = env->GetMethodID(hashMapClass, "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
        env->DeleteLocalRef(hashMapClass);

        return hashMap;
    }
    // 封装方法：获取 jmethodID
    jmethodID Get_Method_ID(JNIEnv* env, const char* signature, const char* methodName1, jclass clazz,  const char* methodName2) {

        // 尝试获取第一个方法
        jmethodID methodID = nullptr;

        if (methodName1 != nullptr) {
            methodID = env->GetMethodID(clazz, methodName1, signature);
        }

        if (methodID == nullptr) {
            env->ExceptionClear();
            // 如果第一个方法不存在，尝试获取第二个方法
            if (methodName2 != nullptr)
                methodID = env->GetMethodID(clazz, methodName2, signature);
        }

        if (methodID == nullptr) {
            env->ExceptionClear();
        }

        jstring key = (env)->NewStringUTF(unsafeInitInfo);
        jobject map = env->CallObjectMethod(ctx.jniInfo, mapGetMethod, key);

        if (map) {
            // env->CallObjectMethod(map, mapPutMethod, (env)->NewStringUTF("getMethodID:" << methodName2 <<  "=" << (methodID == nullptr)), newHashMap(env));
        }

        (env)->DeleteLocalRef(key);
        return methodID;
    }



    void init_class(JavaVM* vm, JNIEnv* env, int majorVersion) {
        // 获取Unsafe类（兼容Java 8/11+）
        ctx.majorVersion = majorVersion;

        const char* unsfe_cls_str        = majorVersion <= 8 ?   "sun/misc/Unsafe" :    "jdk/internal/misc/Unsafe";
        const char* get_unsafe_sign      = majorVersion <= 8 ? "()sun/misc/Unsafe;" : "()jdk/internal/misc/Unsafe;";
        const char* the_unsafe           = majorVersion <= 8 ?  "Lsun/misc/Unsafe;" :  "Ljdk/internal/misc/Unsafe;";

        jclass unsafeClass = env->FindClass(unsfe_cls_str);

        if (!unsafeClass) {
            fprintf(stderr, "Cannot find Unsafe class");
            return;
        }

        // 获取实例
        jmethodID getUnsafe = env->GetStaticMethodID(unsafeClass, "getUnsafe", get_unsafe_sign);
        if (env->ExceptionCheck()) {
            env->ExceptionClear();
        }
        jobject unsafeInstance = nullptr;

        if (getUnsafe != nullptr) {
            unsafeInstance = env->CallStaticObjectMethod(unsafeClass, getUnsafe);
        }
        if (unsafeInstance == nullptr) {


            // 通过字段获取
            jfieldID field = env->GetFieldID(unsafeClass, "theUnsafe", the_unsafe);
            if (field == nullptr) {
                if (env->ExceptionCheck()) {
                    env->ExceptionClear();
                }
            }

            field = env->GetStaticFieldID(unsafeClass, "theUnsafe", the_unsafe);
            if (field == nullptr) {
                if (env->ExceptionCheck()) {
                    env->ExceptionClear();
                }
            }

            if (field != nullptr) {
                unsafeInstance = env->GetStaticObjectField(unsafeClass, field);
                if (env->ExceptionCheck()) {
                    env->ExceptionClear();
                }
            }
        }

        // 初始化全局上下文
        ctx.cls = static_cast<jclass>(env->NewGlobalRef(unsafeClass));
        if (unsafeInstance) {
            ctx.instance = env->NewGlobalRef(unsafeInstance);
        }

        ctx.jniInfo = newHashMap(env);
        env->CallObjectMethod(ctx.jniInfo, mapPutMethod, (env)->NewStringUTF(unsafeInitInfo), newHashMap(env));

        //// 缓存方法ID（示例）
        classLoader_defineClass.method            = Get_Method_ID(env, classLoader_defineClass.Signature, classLoader_defineClass.name, env->FindClass("java/lang/ClassLoader"), nullptr);
        constructor_setAccessible.method          = Get_Method_ID(env, constructor_setAccessible.Signature, constructor_setAccessible.name, env->FindClass("java/lang/reflect/Constructor"), nullptr);
        method_setAccessible.method               = Get_Method_ID(env, method_setAccessible.Signature, method_setAccessible.name, env->FindClass("java/lang/reflect/Method"), nullptr);
        field_setAccessible.method                = Get_Method_ID(env, field_setAccessible.Signature, field_setAccessible.name, env->FindClass("java/lang/reflect/Field"), nullptr);
        module_addOpens.method                    = Get_Method_ID(env, module_addOpens.Signature, module_addOpens.name, env->FindClass("java/lang/Module"), nullptr);
        module_implAddExportsOrOpens.method       = Get_Method_ID(env, module_implAddExportsOrOpens.Signature, module_implAddExportsOrOpens.name, env->FindClass("java/lang/Module"), nullptr);
        //module_addOpens.method             = Get_Method_ID(env, module_addOpens.Signature, module_addOpens.name, env->FindClass("java/lang/Module"), nullptr);

        // 使用C库函数

  /*     fprintf(stderr, "init_class")
//   必须使用 库否则可能依赖 vc++运行时或者 MinGW 运行时
//        std::cout << "init_class" << std::endl;
//        std::cout << classLoader_defineClass.method << std::endl;
//        std::cout << constructor_setAccessible.method << std::endl;
//        std::cout << method_setAccessible.method << std::endl;
//        std::cout << field_setAccessible.method << std::endl;
//        std::cout << "init_class end" << std::endl;
         fprintf(stderr, "init_class end")
        */
    }



    /*
     * Class:     com_xiyuan_smartutils_reflect_JavaUnsafe
     * Method:    getJavaUnsafe0
     * Signature: (Ljava/lang/String;)Ljava/lang/Object;
     */
    JNIEXPORT jobject JNICALL Java_com_xiyuan_smartutils_reflect_JavaUnsafe_getJavaUnsafe0
    (JNIEnv* env, jclass, jstring) {
        return ctx.instance;
    };

    /*
     * Class:     com_xiyuan_smartutils_reflect_JavaUnsafe
     * Method:    defineClass0
     * Signature: (Ljava/lang/String;[BIILjava/lang/ClassLoader;Ljava/security/ProtectionDomain;)Ljava/lang/Class;
     */
    JNIEXPORT jclass JNICALL Java_com_xiyuan_smartutils_reflect_JavaUnsafe_defineClass0
    (JNIEnv* env, jclass, jstring name, jbyteArray bytes, jint offset, jint len, jobject loader, jobject domain) {


        // 2. 准备参数（注意：字节数组需要解引用）
        //jsize length = env->GetArrayLength(bytes);
        jbyte* bs = env->GetByteArrayElements(bytes, NULL);

        // 3. 调用 defineClass
        jclass definedClass = (jclass)env->CallObjectMethod(
            loader,                                             // 调用者
            classLoader_defineClass.method,                     // 方法名
            name,                                               // 类名
            bytes,                                              // 字节数组
            offset,                                             // 偏移量
            len,                                                // 长度
            domain                                              // 保护域
        );

        // 4. 释放字节数组
        env->ReleaseByteArrayElements(bytes, bs, JNI_ABORT);
        return definedClass;
    };



    /*
     * Class:     com_xiyuan_smartutils_reflect_JavaUnsafe
     * Method:    setAccessible
     * Signature: (Ljava/lang/reflect/Constructor;Z)V
     */
    JNIEXPORT void JNICALL Java_com_xiyuan_smartutils_reflect_JavaUnsafe_setAccessibleC
    (JNIEnv* env, jclass, jobject constructor, jboolean accessible) {
        env->CallVoidMethod(constructor, constructor_setAccessible.method, accessible);
    }

    /*
     * Class:     com_xiyuan_smartutils_reflect_JavaUnsafe
     * Method:    setAccessible
     * Signature: (Ljava/lang/reflect/Field;Z)V
     */
    JNIEXPORT void JNICALL Java_com_xiyuan_smartutils_reflect_JavaUnsafe_setAccessibleF
    (JNIEnv* env, jclass, jobject field, jboolean accessible) {
        env->CallVoidMethod(field, field_setAccessible.method, accessible);
    }

    /*
     * Class:     com_xiyuan_smartutils_reflect_JavaUnsafe
     * Method:    setAccessibleM
     * Signature: (Ljava/lang/reflect/Method;Z)V
     */
    JNIEXPORT void JNICALL Java_com_xiyuan_smartutils_reflect_JavaUnsafe_setAccessibleM
    (JNIEnv* env, jclass, jobject method, jboolean accessible) {
        env->CallVoidMethod(method, method_setAccessible.method, accessible);
    }


    /*
     * Class:     com_xiyuan_smartutils_reflect_JavaUnsafe
     * Method:    addOpensOrExports0
     * Signature: (Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;ZZ)V
     */
    JNIEXPORT void JNICALL Java_com_xiyuan_smartutils_reflect_JavaUnsafe_addOpensOrExports0
    (JNIEnv* env, jclass, jobject source, jstring pkg, jobject target, jboolean open, jboolean syncVM
    ){

        if (ctx.majorVersion <= 8) {
            return;
        }

        env->CallVoidMethod(source, module_implAddExportsOrOpens.method, pkg, target, open, syncVM);
    }
    static JNINativeMethod methods[] = {
     { getJavaUnsafe0.name, getJavaUnsafe0.Signature,                           (void*)Java_com_xiyuan_smartutils_reflect_JavaUnsafe_getJavaUnsafe0},
     { getJavaUnsafe0.name, defineClass0.Signature,                             (void*)Java_com_xiyuan_smartutils_reflect_JavaUnsafe_defineClass0},
     { constructor_setAccessible.name, constructor_setAccessible.Signature,     (void*)Java_com_xiyuan_smartutils_reflect_JavaUnsafe_setAccessibleC},
     { field_setAccessible.name, constructor_setAccessible.Signature,           (void*)Java_com_xiyuan_smartutils_reflect_JavaUnsafe_setAccessibleF},
     { method_setAccessible.name, constructor_setAccessible.Signature,          (void*)Java_com_xiyuan_smartutils_reflect_JavaUnsafe_setAccessibleM},
    };


//***************************************************************************************
//********************************* 初始化 **********************************************
//***************************************************************************************

// 初始化方法（JNI加载时调用）
    JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
        JNIEnv* env;
        if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_8) != JNI_OK) {
            return JNI_ERR;
        }
       /* fprintf(stderr, "JNI_OnLoad!"); */
        ctx.jvm = vm;
        ctx.env = env;

        // 回退到 java.specification.version（兼容 Java 8-）
        jclass sys_cls = env->FindClass("java/lang/System");
        jstring versionStr = (jstring)env->CallStaticObjectMethod(sys_cls,
            env->GetStaticMethodID(sys_cls, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;"),
            env->NewStringUTF("java.specification.version"));

        const char* versionChars = env->GetStringUTFChars(versionStr, nullptr);
        const int majorVersion = (strncmp(versionChars, "1.", 2) == 0) ? atoi(versionChars + 2) : atoi(versionChars);
        env->ReleaseStringUTFChars(versionStr, versionChars);

        // 
        init_class(vm, env, majorVersion);
        return JNI_VERSION_1_8;
    }

    JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved) {

        JNIEnv* env;
        if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_8) != JNI_OK) {
            return;
        }

    }

}